#include "part2d_cache.h"

#include "script/scripthelper.h"
#include "script/file_list.h"
#include "vfs/vfs.h"

#include "util/log.h"		    // log
#include "util/common_macros.h"		// macros
#include "util/mmgr.h"		    // Needs to come in early



using namespace part2d;


static void DeletePartData(Part2DCache::PART_CACHE* pCacheElement, void* pvData)
{
	assert(pCacheElement && "Cache element is invalid!");

	for (int i = 0; i < (int)pCacheElement->Cache.size(); i++)
		delete pCacheElement->Cache[i];
	pCacheElement->Cache.clear();
}



Part2DCache::Part2DCache() : m_MinCacheSize(0)
{
}

Part2DCache::~Part2DCache()
{
	m_Cache.ForEach(DeletePartData, (void*)this);
}

Part2DCache& Part2DCache::Instance()
{
	static Part2DCache inst;
	return inst;
}

Part2D* Part2DCache::GetPart(const std::string& File, unsigned int CustomCacheSize, bool PreallocateOnly)
{
	PART_CACHE* pCache = m_Cache.GetElement(File);
	if (!pCache)
		pCache = CreateNewElement(File, CustomCacheSize);
	if (!pCache || !pCache->IsValid)
		return NULL;

	if (CustomCacheSize > 0) pCache->CustomCacheSize = CustomCacheSize;

	if (pCache->Cache.empty())
	{
		PreallocateCache(pCache, 1);
		assert(!pCache->Cache.empty() && "Part cache is empty!");
	}

	if (PreallocateOnly) return NULL;

	Part2D* pRet = pCache->Cache[0];
	pCache->Cache.erase(pCache->Cache.begin());
	return pRet;
}

void Part2DCache::Release(Part2D* pPart)
{
	if (!pPart) return;

	PART_CACHE* pCache = m_Cache.GetElement(pPart->GetFile());
	if (!pCache || !pCache->IsValid)
	{
		// doesn't belong... just delete the pointer and be done with it?
		delete pPart;
		return;
	}
	
	// clear resources and return to cache
	pPart->Clear();
	for (int i = 0; i < pPart->GroupC(); i++)
		pPart->GetGroup(i)->ResetTimer();
	pCache->Cache.push_back(pPart);
}

void Part2DCache::CacheFolder(const std::string& Folder, unsigned int CustomCacheSize)
{
	assert(!Folder.empty());
	
	std::vector<std::string> Files;

	if (vfs::VFS::Instance().BuildFileList(Files, Folder, "p2d") > 0)
	{
		for (int i = 0; i < (int)Files.size(); i++)
			GetPart(Files[i], CustomCacheSize, true);
	}
	else
	{
		script::FileList List;
		List.Build(Folder + "/*.p2d");

		for (int i = 0; i < List.GetFileC(); i++)
			GetPart(List.GetFileName(i), CustomCacheSize, true);
	}
}

Part2DCache::PART_CACHE* Part2DCache::CreateNewElement(const std::string& Name, unsigned int CustomCacheSize)
{
	PART_CACHE* pCache = new PART_CACHE;
	pCache->IsValid = pCache->Part.Load(Name);
	if (pCache->IsValid)
	{
		pCache->CustomCacheSize = CustomCacheSize;
		PreallocateCache(pCache);
	}
	else
	{
		// invalid file;
		// but still keep it so we don't keep trying to reload the same shit over and over
		_LOG(MSG_WARNING, "Attempt to cache invalid part2d file " << Name << " ! ");
	}
	m_Cache.AddElement(Name, pCache);
	return pCache;
}

void Part2DCache::PreallocateCache(PART_CACHE* pCache, unsigned int ForceMin)
{
	assert(pCache && pCache->IsValid && "Cache element is invalid!");

	unsigned int Size = UTIL_HIGH( ForceMin, m_MinCacheSize );
	Size = UTIL_HIGH( Size, pCache->CustomCacheSize );

	while (pCache->Cache.size() < Size)
	{
		Part2D* pPart = pCache->Part.Clone();
		pCache->Cache.push_back(pPart);
	}
}
